package com.atguigu.tingshu.order.helper;

import com.atguigu.tingshu.common.execption.BusinessException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.MD5;
import com.atguigu.tingshu.common.utils.ThrowUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.TreeMap;

/**
 * 签名加密和校验工具类
 */
@Slf4j
public class SignHelper {

    /**
     * 签名密钥
     */
    public static final String SECRET_KEY = "tingshu";

    /**
     * 签名有效时间（10分钟，单位：毫秒）
     */
    private static final long SIGN_VALID_TIME = 10 * 60 * 1000;

    /**
     * 对Map参数进行签名加密
     *
     * @param paramMap 参数Map
     * @return 加密后的签名字符串
     */
    public static String generateSign(Map<String, Object> paramMap) {
        if (paramMap == null || paramMap.isEmpty()) {
            log.warn("参数Map为空，无法生成签名");
            return "";
        }

        // 1. 使用TreeMap进行排序
        TreeMap<String, Object> sortedMap = new TreeMap<>(paramMap);

        // 2. 拼接所有值
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> entry : sortedMap.entrySet()) {
            Object value = entry.getValue();
            if (value != null) {
                sb.append(value);
            }
        }

        // 3. 添加密钥
        sb.append(SECRET_KEY);

        // 4. MD5加密
        String signStr = sb.toString();
        String sign = MD5.encrypt(signStr);

        log.debug("生成签名 - 原始字符串: {}, 签名: {}", signStr, sign);
        return sign;
    }

    /**
     * 校验签名是否正确
     * 如果校验失败，直接抛出异常
     *
     * @param paramMap 参数Map（不包含sign字段）
     * @param sign     待校验的签名
     */
    public static void verifySign(Map<String, Object> paramMap, String sign) {
        // 1. 参数校验
        ThrowUtil.throwIf(sign == null || sign.isEmpty(), ResultCodeEnum.SIGN_ERROR);
        ThrowUtil.throwIf(paramMap == null || paramMap.isEmpty(), ResultCodeEnum.SIGN_ERROR);

        // 2. 校验时间戳是否存在
        ThrowUtil.throwIf(!paramMap.containsKey("timestamp"), ResultCodeEnum.SIGN_ERROR);
        Object timestampObj = paramMap.get("timestamp");
        ThrowUtil.throwIf(timestampObj == null, ResultCodeEnum.SIGN_ERROR);

        // 3. 解析时间戳
        long timestamp = parseTimestamp(timestampObj);

        // 4. 校验时间戳有效性
        long currentTime = System.currentTimeMillis();
        long timeDiff = currentTime - timestamp;
        ThrowUtil.throwIf(timeDiff < 0, ResultCodeEnum.SIGN_ERROR);
        ThrowUtil.throwIf(timeDiff > SIGN_VALID_TIME, ResultCodeEnum.SIGN_OVERDUE);
        log.debug("时间戳校验通过 - 时间戳: {}, 相差: {}ms", timestamp, timeDiff);
        // 5. 校验签名
        String newSign = generateSign(paramMap);
        ThrowUtil.throwIf(!newSign.equals(sign), ResultCodeEnum.SIGN_ERROR);
        log.debug("签名校验成功");
    }

    /**
     * 解析时间戳
     *
     * @param timestampObj 时间戳对象
     * @return 时间戳长整型值
     */
    private static long parseTimestamp(Object timestampObj) {
        try {
            return Long.parseLong(timestampObj.toString());
        } catch (NumberFormatException e) {
            log.error("timestamp字段格式错误: {}", timestampObj);
            throw new BusinessException(ResultCodeEnum.SIGN_ERROR);
        }
    }
}
